package com.bootx.predict;

import com.bootx.predict.pojo.PredictPlugin;
import com.bootx.predict.pojo.PredictionResult;
import com.bootx.predict.pojo.RedPacketBatch;
import com.bootx.predict.pojo.RedPacketRecord;
import com.bootx.predict.util.PredictUtils;
import org.springframework.stereotype.Component;

import java.util.*;

@Component("trendSlopePlugin")
public class TrendSlopePlugin extends PredictPlugin {

    private final int windowSize = 5;
    private final double slopeFactor = 8;

    @Override
    public String getName() {
        return "trendSlopePlugin";
    }

    @Override
    public List<PredictionResult> predict(List<RedPacketBatch> list, Set<Integer> indexes) {
        List<RedPacketRecord> history = PredictUtils.parseData(list);
        // 按轮次和index分组
        Map<Integer, Map<Long, List<RedPacketRecord>>> grouped = new HashMap<>();
        for (RedPacketRecord r : history) {
            grouped.computeIfAbsent(r.getIndex(), k -> new HashMap<>())
                    .computeIfAbsent(r.getRoundId(), k -> new ArrayList<>())
                    .add(r);
        }

        List<PredictionResult> results = new ArrayList<>();
        for (int idx : indexes) {
            Map<Long, List<RedPacketRecord>> rounds = grouped.getOrDefault(idx, Collections.emptyMap());
            if (rounds.isEmpty()) {
                results.add(new PredictionResult(idx, 0, 0, 0.5, 0));
                continue;
            }

            // 最近 windowSize 轮排序
            List<Long> sortedRounds = new ArrayList<>(rounds.keySet());
            Collections.sort(sortedRounds);
            int size = sortedRounds.size();
            int start = Math.max(0, size - windowSize);
            List<Long> recentRounds = sortedRounds.subList(start, size);

            List<Double> ratios = new ArrayList<>();
            List<Long> xList = new ArrayList<>();
            for (int i = 0; i < recentRounds.size(); i++) {
                Long roundId = recentRounds.get(i);
                List<RedPacketRecord> recs = rounds.get(roundId);
                int total = recs.size();
                int oddCount = (int) recs.stream().filter(r -> PredictUtils.isAmountOdd(r.getAmount())).count();
                ratios.add(oddCount / (double) total);
                xList.add((long) i);
            }

            if (ratios.isEmpty()) {
                results.add(new PredictionResult(idx, 0, 0, 0.5, 0));
                continue;
            }

            // 计算斜率
            double slope = linearRegressionSlope(xList, ratios);

            // 基础概率取最后一个轮次比例
            double baseProb = ratios.get(ratios.size() - 1);
            // 调整概率
            double predictedProb = baseProb + slopeFactor * slope;

            if (predictedProb > 0.99) {
                predictedProb = 0.99;
            }
            if (predictedProb < 0.01) {
                predictedProb = 0.01;
            }

            // 统计总次数和奇数次数
            int total = 0, oddCount = 0;
            for (List<RedPacketRecord> recs : rounds.values()) {
                total += recs.size();
                oddCount += recs.stream().filter(r -> PredictUtils.isAmountOdd(r.getAmount())).count();
            }
            double avgOpenTime = rounds.values().stream()
                    .flatMap(Collection::stream)
                    .mapToInt(RedPacketRecord::getOpenTime)
                    .average()
                    .orElse(0);

            results.add(new PredictionResult(idx, total, oddCount, predictedProb, Double.valueOf(avgOpenTime+"").intValue()));
        }
        return results;
    }
    private double linearRegressionSlope(List<Long> x, List<Double> y) {
        int n = x.size();
        if (n == 0) {
            return 0;
        }
        double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
        for (int i = 0; i < n; i++) {
            sumX += x.get(i);
            sumY += y.get(i);
            sumXY += x.get(i) * y.get(i);
            sumX2 += x.get(i) * x.get(i);
        }
        double numerator = n * sumXY - sumX * sumY;
        double denominator = n * sumX2 - sumX * sumX;
        if (denominator == 0) {
            return 0;
        }
        return numerator / denominator;
    }
}
